Odtwarzanie i rejestrowanie dźwięku na niskim poziomie. I [WinApi][CBuilder]
Krecik
Napisałem ten artykuł, ponieważ nie mogłem swego czasu w polskim internecie znaleźć konkretnych informacji na temat "ręcznego" przetwarzaniu dźwięku (sample by sample, czyli operowaiu na najmniejszych jednostkach dźwięku cyfrowego w PCM [PulseCodeModulation] - próbkach).
Przy okazji postanowiłem, chociaż jedną nogą, wrócić na łamy tego servisu [co po starsi userzy ;), mam nadzieję, jeszcze mnie pamiętą - pozdrowienia :-)].
Seria będzie składała się z trzech części:
- Omówienie formatu Wave w systemie Windows (w tym trochę o RIFF) <small>[TEN ARTYKUŁ]</small>
- O odtwarzaniu dźwięku przez funkcje waveOut...
- Nagrywanie dźwięku przez waveIn...
//----------------------------------------------------------------
Microsoft opracował ogólną strukturę plików do wymiany danych (głównie multimedia) w systemie Windows. Ogólna nazwa standardu to RIFF (Resource Interchange File Format). Plik taki składa się z większej liczby pakietów (Chunk) mających własne nazwy. Nazwa pakietu musi składać się z czterech znaków, w tym co najmniej jedna litera (pozostałe znaki muszą być wtedy spacjami). Spacja nie może rozdzielać liter w nazwie pakietu. Nazwy pisane dużymi literami (jak RIFF, WAVE, ...) muszą być zarejestrowane przez Microsoft, by były standardowe i jednoznaczne. Po nazwie pakietu następuje 4-bajtowa liczba określająca długość pakietu (bez 4 bajtów nazwy i 4 bajtów określających długość pakietu).
Co prawda API systemu Windows (w bibliotece mmsystem.h) zawiera specjalne funkcje do obsługi plików zapisanych w formacie RIFF (jak mmioOpen, mmioDescent,itp...), jednak uznałem, że w wypadku dość prostego formatu wave nie będę ich opisywał (materiał na osobny artykuł).
Prostszym i w tym wypadku chyba klarowniejszym sposobem jest stworzenie struktury nagłówka pliku Wave i wczytanie do niej danych z pliku.
Wewnętrzną budowę tego formatu można by przedstawić następująco:
<small>
Sorry, ale przesuncie sobie text parę linijek w dół, bo kochany skrypcik tak robi z tabelkami... :(
[To się jakoś dało ominąć, ale nie pamiętam jak, a jestem teraz baaardzo śpięcy i zaraz padnę twarzą w keyboard.]</small>
offset | nazwa pola | wielkość w bajtach, typ | opis |
0 | ChunkID | 4, char[4] | Podstawowy pakiet w pliku {'R','I','F','F'} (zawiera dwie podgrupy: "fmt " i "data") |
4 | ChunkSize | 4, unsigned long | Określa rozmiar pakietu RIFF |
8 | Format | 4, char[4] | Określa format pliku {'W','A','V','E'} |
12 | Subchunk1ID | 4, char[4]/td> | Nazwa pakietu zawierającego dane o formacie dźwięku (jakość) {'f','m','t',' '} |
16 | Subchunk1Size | 4, unsigned long | Rozmiar pakietu |
20 | AudioFormat | 2, unsigned short | Format dźwieku (1-bez kompresji, standard) |
22 | NumChannel | 2, unsigned short | Liczba kanałów (1-mono, 2-stereo) |
24 | SampleRate | 4, unsigned long | Szybkość próbkowania |
28 | ByteRate | 4, unsigned long | Liczba bajtów na sekundę... Wyzncznik jakości i czasem służy do wyznaczania czasu trwania dźwięku |
32 | BlockAlign | 2, unsigned short | (NumChannels * BitsPerSample) / 8 |
34 | BitsPerSample | 2, unsigned short | Ilość bitów przypadających na każdą próbkę dźwięku |
36 | Subchunk2ID | 4, char[4] | Nazwa pakietu z danymi {'d','a','t','a'} |
40 | Subchunk2Size | 4, unsigned long | Ilość danych... Rozmiar dźwięku</td |
44 | data | Subchunk2Size, char[Subchunk2Size] | Kolejne próbki dźwięku... |
Muszę tu się jeszcze wytłumaczyć... Tak naprawdę powyższe przedstawienie pomija kilka pól, ponieważ nie występują one w standardowym formacie zapisu (bez kompresji), ale uprzedzam, że mogą się pojawić w jakichś niestandardowych formatach.
Jak już wspomniałem istnieją funkcje w mmsystem, które bardzo ładnie operują na typie RIFF, ale nam tylko zaciemniłyby program.
Ja przedstawiam inny sposób, strukturę o takich polach jak wymagane przez standard...
W języku C++ mogła by ona wyglądać np. tak (dodatkowy podział na 3 wewnętrzne sekcje jest mój własny)
typedef struct _WAVEHEAD {
struct {
char RIFF[4];
unsigned long Size;
char WAVE[4];
} RIFF;
struct {
char fmt[4] ;
unsigned long BlockSize;
struct {
unsigned short AudioFormat;
unsigned short NumChannels;
unsigned long SampleRate;
unsigned long ByteRate;
unsigned short BlockAlign;
unsigned short BitsPerSample;
} Format;
} fmt;
struct {
char data[4];
unsigned long DataSize;
} data;
} WAVEHEAD;
a przykładowa funkcja wczytująca tą strukturę z pliku, np tak:
#include <stdio.h>
#include <mmsystem.h>
WAVEHEAD head; //nasza struktura
WAVEFORMATEX format; //trochę z wyprzedzeniem, ponieważ ta struktura
// pochodzi z mmsystem i będzie dotyczyć kolejnych art.
char *FileName = "c:\\blabla.wav";
FILE *plik = fopen(FileName,"r");
fread(&head, sizeof(WAVEHEAD), 1, plik);
memcpy(&format,&(head.fmt.Format),sizeof(WAVEFORMATEX));
buffer = new char[head.data.DataSize]; // i zaś się trochę do przodu wyrwałem, bo to są już dane dźwięku, które wykorzystamy wkrótce
fread(buffer,head.data.DataSize,1,plik);
fclose(plik);
delete[] Buffer;
No i to by było na tyle w dniu dzisiejszym... Kolejny art (odtwarzanie dźwięku) w najbliższej przyszłości... Pozdrawiam...
PS> Bardzo przepraszam za ewentualne błędy ort!, naprawde nie mam nigdzie pod bliższą i dalszą ręką MSWord`a... Sorry... Poprawie jak tylko znajdę kolejną chwilę wolnego czasu.
właśnie poszukuje takiego programu do tej tabelki w C++ na komponentach może ma ktoś taki niech napisze z góry dziękuje pozdro !!!!!!!!!!!!!!!!!!!!!!!!!!!!!
postawiłem 5 na zachęte - mysle, że coś z tego będzie :D